package com.moliku.insurance.common.query;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.google.common.collect.Lists;
import com.moliku.insurance.common.util.MapperUtils;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import java.util.List;

/**
 * 分页对象
 *
 * @author Mr.Liu
 * @since 2020-02-01
 */
@SuppressWarnings("unused")
@Slf4j
@Data
@Accessors(chain = true)
@NoArgsConstructor
@JsonInclude(JsonInclude.Include.NON_NULL)
@ApiModel(value = "接口分页对象", description = "接口分页对象")
public class Pagination<T> {
    private static final Long DEFAULT_CURRENT = 0L;
    private static final Long DEFAULT_SIZE = 10L;
    private static final String DEFAULT_DESC = "descend";
    private static final String DEFAULT_ASC = "ascend";

    @ApiModelProperty(value = "当前页码", required = true)
    private Long pageNo = 1L;

    @ApiModelProperty(value = "每页显示条数", required = true)
    private Long pageSize = 10L;

    @ApiModelProperty(value = "排序字段", hidden = true)
    private String sortField;

    @ApiModelProperty(value = "排序方式，正序|倒序", hidden = true)
    private String sortOrder;

    public Pagination(Integer current, Integer size) {
        this.pageNo = current == null ? DEFAULT_CURRENT : current;
        this.pageSize = size == null ? DEFAULT_SIZE : size;
    }

    /**
     * 将mybatis plus 分页结果转换成 web 前端所需要的 分页响应格式
     *
     * @param page 分页结果集
     * @param <T>  数据类型
     * @return web 分页结果集响应
     */
    public static <T> Pagination<T> asWebPagination(IPage<T> page) {
        return new WebPagination<>(page);
    }

    public IPage<T> ofPage() {
        return ofPage(null, null);
    }

    public IPage<T> ofPage(String defaultSortField, String defaultSortOrder) {
        this.pageNo = pageNo == null ? DEFAULT_CURRENT : pageNo;
        this.pageSize = pageSize == null ? DEFAULT_SIZE : pageSize;
        IPage<T> page = new Page<>(pageNo, pageSize);
        List<OrderItem> orderItems = buildOrderItem(sortField, sortOrder, defaultSortField, defaultSortOrder);
        page.orders().addAll(orderItems);
        return page;
    }

    public PageData<T> ofPageData(IPage<T> page) {
        return new PageData<T>(page, page.getRecords());
    }

    public <T, E> PageData<E> ofPageData(IPage<T> page, Class<E> clazz) {
        List<E> records = MapperUtils.INSTANCE.mapAsList(page.getRecords(), clazz);
        return new PageData<E>(page, records);
    }

    private List<OrderItem> buildOrderItem(String sortField, String sortOrder, String defaultSortField, String defaultSortOrder) {
        List<OrderItem> orderItems = Lists.newArrayList();
        if (StringUtils.isNotBlank(sortField)) {
            sortField = com.baomidou.mybatisplus.core.toolkit.StringUtils.camelToUnderline(sortField);
            boolean isDesc = StringUtils.isNotBlank(sortOrder) && DEFAULT_DESC.equals(sortOrder);
            orderItems.add(isDesc ? OrderItem.desc(sortField) : OrderItem.asc(sortField));
        } else if (StringUtils.isNotBlank(defaultSortField)) {
            defaultSortField = com.baomidou.mybatisplus.core.toolkit.StringUtils.camelToUnderline(defaultSortField);
            boolean isDesc = StringUtils.isNotBlank(defaultSortOrder) && DEFAULT_DESC.equals(defaultSortOrder);
            orderItems.add(isDesc ? OrderItem.desc(defaultSortField) : OrderItem.asc(defaultSortField));
        }
        return orderItems;
    }

    @Data
    static class PageData<T> {
        private Long pageNo;
        private Long pageSize;
        private Long totalCount;
        private Long totalPage;
        private List<T> data;

        public PageData(IPage<?> page, List<T> data) {
            this.data = data;
            this.pageNo = page.getCurrent();
            this.pageSize = page.getSize();
            this.totalCount = page.getTotal();
            this.totalPage = page.getPages();
        }
    }

    /**
     * Web 分页结果封装
     */
    @Data
    @EqualsAndHashCode(callSuper = true)
    static class WebPagination<T> extends Pagination<T> {
        //总记录数
        private Long total = 0L;
        //总页数
        private Long pages = 1L;
        //结果集
        private List<T> data;
        // 首页
        private Long firstPage = 1L;
        // 尾页
        private Long lastPage = 0L;
        // 上一页
        private Long prePage = 0L;
        // 下一页
        private Long nextPage = 0L;
        // 导航页码数
        private int navigatePages = 8;
        // 所有导航页号
        private Long[] navigatePageNumbers;
        // 是否为第一页
        private boolean isFirstPage = false;
        // 是否为最后一页
        private boolean isLastPage = false;
        // 是否有前一页
        private boolean hasPreviousPage = false;
        // 是否有下一页
        private boolean hasNextPage = false;


        public WebPagination(IPage<T> page) {
            this.data = page.getRecords();
            this.setPageNo(page.getCurrent());
            this.setPageSize(page.getSize());
            this.total = page.getTotal();
            this.pages = page.getPages();
            this.firstPage = 1L;
            this.lastPage = page.getPages();
            this.prePage = page.getCurrent() - 1;
            this.nextPage = page.getCurrent() + 1;

            //    计算导航页
            this.calcNavigatePageNumbers();
            //    判定页面边界
            this.isFirstPage = this.getPageNo() == 1L;
            this.isLastPage = this.getPageNo().equals(this.pages) && this.getPageNo() != 1L;
            this.hasPreviousPage = this.getPageNo() > 1L;
            this.hasNextPage = this.getPageNo() < this.pages;
        }

        /**
         * 计算导航页
         */
        private void calcNavigatePageNumbers() {
            //  总页码小于导航页码数
            if (this.pages < this.navigatePages) {
                int page = this.pages.intValue();
                this.navigatePageNumbers = new Long[page];
                for (int i = 0; i < page; i++) {
                    this.navigatePageNumbers[i] = i + 1L;
                }
            } else {
                //    总页码大于导航页码数
                this.navigatePageNumbers = new Long[this.navigatePages];
                long startNum = this.getPageNo() - this.navigatePages / 2;
                long endNum = this.getPageNo() + this.navigatePages / 2;
                if (startNum < 1) {
                    startNum = 1L;
                    for (int i = 0; i < this.navigatePages; i++) {
                        this.navigatePageNumbers[i] = startNum++;
                    }
                } else if (endNum > this.pages) {
                    endNum = this.pages;
                    for (int i = this.navigatePages - 1; i >= 0; i--) {
                        this.navigatePageNumbers[i] = endNum--;
                    }
                } else {
                    for (int i = 0; i < this.navigatePages; i++) {
                        this.navigatePageNumbers[i] = startNum++;
                    }
                }
            }
        }
    }
}
